﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using BlueCMS.Core.Entities;

namespace BlueCMS.Core.Pages
{
    /// <summary>
    /// TemplateParser takes the textual representation of a Template and creates the TemplateFraments that represent that Template.
    /// </summary>
    public class TemplateParser
    {
        static readonly string TagPattern = @"<contentregion\s*(?<parameters>.*?)>";
        static readonly RegexOptions TagOptions = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;
        static readonly Regex TagMatcher;

        static TemplateParser()
        {
            TagMatcher = new Regex(TagPattern, TagOptions);
        }

        // TODO: Do more validation here, check for multiple regions with same name and for unknown parameters.
        // It would be nice to add some sort of line number to the error output
        public static bool IsValid(string input, out List<string> errors)
        {
            errors = new List<string>();

            if (input.Length == 0)
            {
                errors.Add("Cannot parse an empty layout");
                return false;
            }

            Match match = TagMatcher.Match(input);
            int position = 0;
            while (match.Success)
            {
                Dictionary<string, string> parameters = ParseParameterString(match.Groups["parameters"].Value);

                if (!parameters.ContainsKey("region"))
                {
                    errors.Add("Content region requires \"region\" paramtere");
                    return false;
                }

                // Keep moving
                position = match.Index + match.Length;
                match = match.NextMatch();
            }

            return true;
        }

        public static IList<TemplateFragment> Parse(string input, Template template)
        {
            Trace.Assert(input.Length > 0, "Cannot parse an empty template.");

            IList<TemplateFragment> list = new List<TemplateFragment>();

            TemplateFragment fragment;
            Match match = TagMatcher.Match(input);
            int position = 0, count = 0;
            while (match.Success)
            {
                // Check for a literal before the match
                if (position < match.Index)
                {
                    fragment = new TemplateFragment();
                    fragment.template_id = template.template_id;
                    fragment.type_cd = "LITERAL";
                    fragment.displayorder = count++;
                    fragment.literal = input.Substring(position, match.Index - position);
                    list.Add(fragment);
                }

                Dictionary<string, string> parameters = ParseParameterString(match.Groups["parameters"].Value);
                fragment = new TemplateFragment();
                fragment.type_cd = "CONTENT_REGION";
                fragment.template_id = template.template_id;
                fragment.displayorder = count++;
                fragment.ismasterregion = false;
                fragment.region_nm = parameters["region"];
                list.Add(fragment);
                if (parameters.ContainsKey("ismasterregion"))
                {
                    fragment.ismasterregion = true;
                    if (parameters["ismasterregion"].Length > 0)
                    {
                        bool isMasterRegion;
                        Boolean.TryParse(parameters["ismasterregion"], out isMasterRegion);
                        fragment.ismasterregion = isMasterRegion;
                    }
                }

                // Keep moving
                position = match.Index + match.Length;
                match = match.NextMatch();
            }

            // Catch the final literal if it's there
            if (position < input.Length)
            {
                fragment = new TemplateFragment();
                fragment.template_id = template.template_id;
                fragment.type_cd = "LITERAL";
                fragment.displayorder = count++;
                fragment.literal = input.Substring(position, input.Length - position);
                list.Add(fragment);
            }

            return list;
        }

        protected static Dictionary<string, string> ParseParameterString(string parameters)
        {
            Dictionary<string, string> list = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

            if (parameters.Length > 0)
            {
                int position = 0;
                char[] from = parameters.ToCharArray();

                while (position < from.Length)
                {
                    string name = ParseAttributeName(ref position, from);

                    if (position >= parameters.Length)
                    {
                        list[name] = "";
                        break;
                    }

                    string value = ParseAttributeValue(ref position, from);

                    list[name] = value;
                }
            }

            return list;
        }

        protected static string ParseAttributeName(ref int position, char[] from)
        {
            StringBuilder output = new StringBuilder();

            EatWhitespace(ref position, from);

            while (position < from.Length)
            {
                if (IsWhitespace(from[position]) || from[position] == '=')
                {
                    break;
                }
                output.Append(from[position]);
                position++;
            }

            EatWhitespace(ref position, from);

            return output.ToString();
        }

        protected static string ParseAttributeValue(ref int position, char[] from)
        {
            StringBuilder output = new StringBuilder();

            if (from[position] == '=')
            {
                position++;
                EatWhitespace(ref position, from);
                if (from[position] == '"' || from[position] == '\'')
                {
                    char delimiter = from[position];
                    position++;
                    while (position < from.Length && from[position] != delimiter)
                    {
                        output.Append(from[position]);
                        position++;
                    }
                    position++;
                }
                else
                {
                    while (position < from.Length && !IsWhitespace(from[position]))
                    {
                        output.Append(from[position]);
                        position++;
                    }
                }
                EatWhitespace(ref position, from);
            }
            return output.ToString();
        }

        protected static void EatWhitespace(ref int position, char[] from)
        {
            while (position < from.Length)
            {
                if (!IsWhitespace(from[position]))
                {
                    return;
                }
                position++;
            }
        }

        protected static bool IsWhitespace(char c)
        {
            return ("\t\n\r ".IndexOf(c) != -1);
        }
    }
}